from server_send_and_receive_test import *
from game import *
from player import *
from time import sleep
import time
import sys
import random
#SQL imports
from sqlalchemy import create_engine
from sqlalchemy import Table,Column,Integer,String,MetaData,ForeignKey,Float
from sqlalchemy.orm import mapper
from sqlalchemy.orm import sessionmaker
from player import Player
#Class imports
from card import *
from deck import *
from player import *
from server_send_and_receive_test import *
from score import *

badwords={"fucker":"flipper","shit":"shiznit"}


#connect to player database
#create session
engine = create_engine("sqlite+pysqlite:///database.db",echo = True)
Session = sessionmaker(bind=engine)
session = Session()

metadata = MetaData()
#name table, set columns
drazah_players = Table("players",metadata,
                       Column("ID",Integer),
                       Column("name",String,primary_key = True),
                       Column("wins",Integer),
                       Column("losses",Integer),
                       Column("draws",Integer),
                       Column("score",Float),
                       Column("achievements", Integer)
                       )

#Map class to db
mapper(Player,drazah_players)
Session.configure(bind=engine)


# ----------#
#Start Drazah
rounds=8
replayWindow=20

#main class
class Game(object):
    def __init__(self):

        #declaring pipe
        self.pipe = ServerPipe("",10001)

        #player list (empty list)
        self.playerList = []

        #player1(emptry list)
        self.ip = None
        self.port = None
        self.state = "NEW"
        self.buildDecks()
        self.stack=[]

        #Set up 2 connections
        self.scoreMatrixSH = ScoreMatrix("../data/situation_hazard_pairs.csv")
        self.scoreMatrixHA = ScoreMatrix("../data/hazard_action_pairs.csv")

        #When scores are calculated, drop them in here as a list of two.
        #Gets used when sending round debrief information
        self.scoreTmp=None
        self.timeout=None
        self.recycle=[]
        self.id=random.randint(0,100000)
    #Set ip and port
    def setupConnection(self,ip,port):
        self.ip = ip
        self.port = port
    
    def startConn(self):
        self.pipe.startConnection(self.ip,self.port)
        self.pipe.recvData()
         
        #get 2 players
        #check to see how many players are currently in the game, if there is only 1 the game will add another,
        # if there is more than 2 players the game will inform the server of this

    def printPlayers(self):
        print self.playerList
        #build decks
        #Function that creates 3 instances of the `Deck' object and populates them with items

    def buildDecks(self):
        #build Hazard deck and populate with cards using the information from `hazarCardsContent.csv'
        #file in the /data folder. (open file>>set range (number of cards)>>read each line>>append to list)
        self.H_deck=Deck("Hazard")
        f = open('../data/hazardCardsContent.csv')
        for i in range (1,27):
            line = f.readline().strip()
            items = line.split("#")
            h=Hazard(items[0],items[1],"H/"+items[2][:-4],items[2])
            self.H_deck.append(h)
        f.close()
        #build Action deck and populate with cards using the information from `actionCardsContent.csv'
        #file in the /data folder. (open file>>set range (number of cards)>>read each line>>append to list
        self.A_deck=Deck("Action")
        f = open('../data/actionCardsContent.csv')
        for i in range (1,28):
            line = f.readline().strip()
            items = line.split('#')
            self.A_deck.append(Action(items[0],items[1],"A/"+items[2][:-4],items[2]))
        #build Situation deck and populate with cards using the information from `situationCardsContent.csv'
        #file in the /data folder. (open file>>set range (number of cards)>>read each line>>append to list
        self.S_deck=Deck("Situation")
        f = open('../data/situationCardsContent.csv')
        for i in range (1,rounds):
            line = f.readline().strip()
            items = line.split('#')
            self.S_deck.append(Situation(items[1],items[3],items[0][0]+"/"+items[0][1],items[2]))
## All .csv files were copied and tweaked from those used to create
## the physical cards, card class attributes had to be cut down
## because `#' was used to break up the descriptions for the cards,
## however we do not need them here.

    #allocate cards
    def popR(self, deck):
        try:
            s=random.randint(0,len(deck)-1)
            return deck.pop(s)
        except IndexError:
            raise Exception("Cannot popR - There are no more cards in the deck!")

    #Deal cards
    def dealCards(self):
        #get cards from the H and A decks and put them in
        #the deck list inside playerList(Player)
        if len(self.playerList) == 2:
            for i in range(4):
                for p in self.playerList:
                    p.hDeck.append(self.popR(self.H_deck))
                    p.aDeck.append(self.popR(self.A_deck))
        else:
            raise Exception("A game cannot be started without two players")

    #set Sit card
    #function to play randomly selected situation card
    def pickSitCard(self):       
        picked=self.popR(self.S_deck)
        return picked
            
    #function to add hazard card to players deck after they have played one
    def addHazCard(self):
        pickedH = self.popR(self.H_deck)
        return pickedH

    #function to add action card to players deck after they have played one
    def addActCard(self):
        pickedA = self.popR(self.A_deck)
        return pickedA

    #function to send messages to all players
    def sendAll(self,message):
        for p in self.playerList:
            try:
                p.connection.sendall(message+"\n\r")
            except:
                pass
    #Send status
    def sendStatus(self):
        for p in self.playerList:
            try:
                p.connection.sendall("DECKH:"+str(p.hDeck)+"\n\r")
                p.connection.sendall("DECKA:"+str(p.aDeck)+"\n\r")
                p.connection.sendall("STACK:"+str(self.stack)+"\n\r")
                p.connection.sendall("SCORE:"+str(p.score)+"\n\r")
                p.connection.sendall("STATE:"+str(self.state)+"\n\r")
                p.connection.sendall("ACHIEVEMENTS:"+str(p.achievements)+"\n\r")
                p.connection.sendall("WINS:"+str(p.wins)+"\n\r")
                p.connection.sendall("LOSSES:"+str(p.losses)+"\n\r")
                p.connection.sendall("DRAWS:"+str(p.draws)+"\n\r")
            except:
                raise Exception{"Problem Sending Status to: %s" %p.name}
                # p.connection.sendall("DECKH:"+str(p.hDeck)+"\n\r")
                # p.connection.sendall("DECKA:"+str(p.aDeck)+"\n\r")
                # p.connection.sendall("STACK:"+str(self.stack)+"\n\r")
                # p.connection.sendall("SCORE:"+str(p.score)+"\n\r")
                # p.connection.sendall("STATE:"+str(self.state)+"\n\r")
                # p.connection.sendall("ACHIEVEMENTS:"+str(p.achievements)+"\n\r")
                # p.connection.sendall("DISPLAYINFO:"+str(p.wins)+str(p.losses)+str(p.draws)+"\n\r")
                # p.connection.sendall("WINS:"+str(p.wins)+"\n\r")
                # p.connection.sendall("LOSSES:"+str(p.losses)+"\n\r")
                # p.connection.sendall("DRAWS:"+str(p.draws)+"\n\r")
                pass
        pass
                                
    # STATE MACHINE
    def stateStep(self):
        #print "State step, game:<%s> is in state:\t %s"%(self.id,self.state)
        #print "Current state: %s"%self.state
        data=""
        #Send client their name for display
        if self.state!="POSTGAME":
            for p in self.playerList:
                if p.name.strip()!="":
                    #print "\tSending PLAYER info to",p.name
                    p.connection.sendall("PLAYER: %s"%p.name+"\n\r")
        poinum="1"
        pnoinum="2"
        if self.state!="NEW" and self.state!="POSTGAME":
            #Player of interest
            poi=self.playerList[0]
            pnoi=self.playerList[1]
            if "2" in self.state:
                poi,pnoi=pnoi,poi
                poinum,pnoinum=pnoinum,poinum
        if self.state == "NEW":
            if len(self.playerList) == 2:
                self.dealCards()
                #print "Got two players."
                self.sendAll("MSG:The game begins")
                self.playerList[0].connection.sendall("%s\n\r"%self.playerList[0].hDeck)
                self.playerList[0].connection.sendall("%s\n\r"%self.playerList[0].aDeck)
                self.playerList[1].connection.sendall("%s\n\r"%self.playerList[1].hDeck)
                self.playerList[1].connection.sendall("%s\n\r"%self.playerList[1].aDeck)
                self.sendAll("REQ_NAME")
                self.state="GETINFO"
            else:
                pass
                #print "You need 2 players to start a game"
        if self.state=="GETINFO":
            if self.playerList[0].name!="" and self.playerList[1].name!="":
                for p in self.playerList:                    
                    op=self.playerList[0]
                    if op==p: op=self.playerList[1]
                    #send opponent info to players
                    p.connection.sendall("OPPONENT: %s\n\r"%op.name)
                    p.connection.sendall("OWINS: %s \n\r"%op.wins)
                    p.connection.sendall("OLOSSES: %s \n\r"%op.losses)
                    p.connection.sendall("ODRAWS: %s \n\r"%op.draws)
                    p.connection.sendall("OACHS: %s \n\r"%op.achievements)
                self.state = "W_P1_S"
            else:
                for ii in range(len(self.playerList)):
                    i=self.playerList[ii]
                    #if i.name!="": continue
                    try:
                        data = i.connection.recv(4096)
                    except:
                        data=""
                    if data.startswith("NAME:"):
                        i.name=data[5:]
                        for j in badwords:
                            if i.name.find(j)>-1:
                                i.name=i.name.replace(j,badwords[j])
                        i.name=i.name[:10]
                        dbitems=session.query(Player).filter("name=:name").params(name=i.name).all()
                        if len(dbitems)>0:
                            #Should only be 1 item.  Assuming so.
                            #All hell will break loose if this is not the case.
                            tPlayer=dbitems[0]
                            tPlayer.connection=i.connection
                            tPlayer.hDeck=i.hDeck
                            tPlayer.aDeck=i.aDeck
                            tPlayer.score=0.0
                            tPlayer.connection.sendall("BASE_ACHIEVEMENTS:%d\n"%tPlayer.achievements)
                            self.playerList[ii]=tPlayer
                        else:
                            session.add(i)
                            session.commit()
                            
        elif self.state in ["W_P1_S","W_P2_S"]:
            if len(self.S_deck) > 0:
                #print "Waiting for player %s to play situation card"%poinum
                #send state to clients and wait for P1 to choose card
                self.stack.append(self.pickSitCard())
                #print "Player %s has played card..."%poinum
                self.sendAll("MSG:Player %s has played the situation card %s\n\r"%(poinum,str(self.stack[0])))
                self.sendStatus()
                pnoi.connection.sendall("REQ:H\n\r")
                #Send message to players
                self.state = "W_P%s_H"%pnoinum
            else:
                self.state = "END"
               
                
        elif self.state in ["W_P2_H","W_P1_H"]:
            #Check player 2's pipe to see if it's been played.  If it has, change state
            #Must be non-blocking
            try:
                data = poi.connection.recv(4096)
                if data.startswith("PLAY:"):
                    card = data[5:].strip()
                    #print card
                    if card in poi.hDeck:
                        #take card from player's deck
                        card=poi.hDeck.remove(card)
                        #give player a new card in return
                        poi.hDeck.append(self.addHazCard())
                        #Add it to the pile
                        self.stack.append(card)
                        #print self.stack
                        #Send out new status (using the function we wrote)
                        self.sendStatus()
                        # Send REQ:A to player 1
                        pnoi.connection.sendall("REQ:A\n\r")
                        #Set state to W_P1_A
                        self.state = "W_P%s_A"%pnoinum
                    else:
                        print "Card not in player's deck"

                else:
                    print "While in state %s, got odd data from client: %s"%(self.state,data)
            except:
                #print "No data while in state: %s"%self.state
                sleep(0.5)

#NEW STATE#  
            
        elif self.state in [ "W_P1_A", "W_P2_A"]:
            #check pipe...
            try:
                data = poi.connection.recv(4096)
                if data.startswith("PLAY:"):
                    card = data[5:].strip()

                    if card in poi.aDeck:
                        #remove card from players hand
                        card=poi.aDeck.remove(card)
                        #give player a new card in return
                        poi.aDeck.append(self.addActCard())
                        self.stack.append(card)
                        #print self.stack
                        #Calculate s/h score add to poi
                        s = self.stack[0]
                        h = self.stack[1]
                        score=self.scoreMatrixSH.score(s,h)
                        self.scoreTmp=[score]
                        pnoi.score += score
                        #Check achievement 0
                        if score>=10 and not pnoi.checkAchievement(0):
                            pnoi.addAchievement(0)
                            # pnoi.connection.sendall("ACHGAINED: 0 \n\r")
                        #print "Hand score:",score
                        #Add this to the player's score
                        poi.score + score
                        #Calculate H/A score for pnoi
                        a = self.stack[2]
                        score = self.scoreMatrixHA.score(h,a)
                        #Check achievement 1
                        if score>=10 and poi.checkAchievement(1) !=1:
                            poi.addAchievement(1)
                            # poi.connection.sendall("ACHGAINED: 1 \n\r")
                        #Check achievement 6
                        if pnoi.score > poi.score and self.stack[1].code=="W*" and pnoi.checkAchievement(6) !=1:
                            pnoi.addAchievement(6)
                            # pnoi.connection.sendall("ACHGAINED: 6 \n\r")
                        #Check achievement 7
                        if pnoi.score > poi.score and self.stack[1].code=="H*" and pnoi.checkAchievement(7) !=1:
                            pnoi.addAchievement(7)
                            # pnoi.connection.sendall("ACHGAINED: 7 \n\r")
                        #Add scores
                        self.scoreTmp.append(score)
                        #Add this to player's score
                        poi.score += score
                        #print "Hand score:",score
                        #print "Player 1's current score %d"%(self.playerList[0].score)
                        #print "Player 2's current score %d"%(self.playerList[1].score)
                        #self.sendAll("STATUS: W_P2_S, %s"%str(self.stack))
                        self.sendStatus()
                    self.state = "RDB:W_P%s_S"%pnoinum
            except:
                #print "No data while in state: %s"%self.state
                sleep(0.5)

        elif self.state.startswith("RDB:"):
            #Round debrief
            self.sendStatus()
            self.sendAll("RDEBRIEF:{'stack':[%s], 'scores':%s}"%(self.stack,self.scoreTmp))
            #Clear stack
            self.stack=[]
            self.state=self.state[4:]
            if self.playerList[0].score>self.playerList[1].score:
                self.playerList[0].wins+=1
                self.playerList[1].losses+=1
            elif self.playerList[1].score>self.playerList[0].score:
                self.playerList[1].wins+=1
                self.playerList[0].losses+=1
            else:
                for i in self.playerList:
                    i.draws+=1
            session.commit()
                                
        elif self.state == "END":
            #print "End of game!"

            for p in [[self.playerList[0],self.playerList[1]],
                      [self.playerList[1],self.playerList[0]]]:
                #Check achievement 2
                if p[0].wins == 1 and p[0].checkAchievement(2) != 1:
                    p[0].addAchievement(2)
                #Check achievement 3
                if p[0].wins>=10 and p[0].checkAchievement(3)!=1:
                    p[0].addAchievement(3)
                #Check achievement 4
                if p[0].wins>=50 and p[0].checkAchievement(4) !=1:
                    p[0].addAchievement(4)
                #Check achievement 5
                if p[0].wins > 5 and p[0].wins >= p[0].losses*2 and p[0].checkAchievement(5)!=1:
                    p[0].addAchievement(5)
                #Check achievement 8
                if p[0].score > 30 and p[0].checkAchievement(8)!=1:
                    p[0].addAchievement(8)
                #Check achievement 9
                if p[0].score > 60 and p[0].checkAchievement(9)!=1:
                    p[0].addAchievement(9)
            #Send status and game details to clients

            out="GAMEDETAILS:{'pDetails':[%s]}"%(self.playerList)
            self.sendStatus()
            #print "Im sending: ",out
            self.sendAll(out)
            self.sendStatus()
            self.timeout=time.time()
            self.state="POSTGAME"
        elif self.state=="POSTGAME":
            for i in self.playerList:
                try:
                    data = i.connection.recv(4096)
                except:
                    data=""

                if data.startswith("END:"):
                    request = data[4:].strip()
                    #print request
                    if request == "PLAY AGAIN":
                        #print "Player wants to start a new game"
                        i.newGameReset()
                        self.recycle.append(i)
                        self.playerList.remove(i)
                    elif request == "QUIT":
                        i.connection.close()
                        #print "closing connections"
                        self.playerList.remove(i)
            if time.time()-self.timeout>replayWindow:
                for i in self.playerList:
                    i.connection.close()
                self.playerList=[]
            if len(self.playerList)==0 :
                self.state="DELETE"


#  LocalWords:  sendStatus startswith playerList recv dealCards pickSitCard ip
#  LocalWords:  replayWindow len POSTGAME elif sendAll setupConnection randint
#  LocalWords:  ScoreMatrix buildDecks startConn startConnection recvData popR
#  LocalWords:  pickedA GETINFO aDeck hDeck sendall ForeignKey TODO ServerPipe
#  LocalWords:  checkAchievement addAchievement scoreMatrixHA scoreTmp pnoinum
#  LocalWords:  DISPLAYINFO str OACHS Drazah newGameReset
